using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class MySpawner : MonoBehaviour {
    public static MySpawner spawner;
    public ObjectCache[] caches;
    public Hashtable activeCachedObjects;

    void Awake()
    {
        // Set the global variable
        spawner = this;

        // Total number of cached objects
        int amount = 0;

        // Loop through the caches
        for (var i = 0; i < caches.Length; i++)
        {
            // Initialize each cache
            caches[i].Initialize();

            // Count
            amount += caches[i].maxSize;
        }

        // Create a hashtable with the capacity set to the amount of cached objects specified
        activeCachedObjects = new Hashtable(amount);
    }

    public static GameObject Spawn(GameObject prefab, Vector3 position, Quaternion rotation)
    {
        ObjectCache cache = null;

        // Find the cache for the specified prefab
        if (spawner)
        {
            for (var i = 0; i < spawner.caches.Length; i++)
            {
                if (spawner.caches[i].prefab == prefab)
                {
                    cache = spawner.caches[i];
                }
            }
        }

        // If there's no cache for this prefab type, just instantiate normally
        if (cache == null)
        {
            return Instantiate(prefab, position, rotation) as GameObject;
        }
        // Find the next object in the cache
        GameObject obj = cache.GetNextObjectInCache();
        // Set the position and rotation of the object
        obj.transform.position = position;
        obj.transform.rotation = rotation;

        // Set the object to be active
        obj.SetActiveRecursively(true);
        spawner.activeCachedObjects[obj] = true;

        return obj;
    }

    public static void Destroy(GameObject objectToDestroy)
    {
        if (spawner && spawner.activeCachedObjects.ContainsKey(objectToDestroy))
        {
            objectToDestroy.SetActiveRecursively(false);
            spawner.activeCachedObjects[objectToDestroy] = false;
        }
        else
        {
            Object.Destroy(objectToDestroy);
        }

    }




    [System.Serializable]
    public class ObjectCache
    {
        public GameObject prefab;
        public int minSize = 0;
        public int maxSize = 50;

        private List<GameObject> objects;
        private int cacheIndex = 0;

        internal void Initialize()
        {
            objects = new List<GameObject>(minSize);

            for (var i = 0; i < minSize; i++)
            {
                GameObject obj = MonoBehaviour.Instantiate(prefab) as GameObject;
                obj.SetActiveRecursively(false);
                obj.name = obj.name + i;
                objects.Add(obj);
            }
        }

        internal GameObject GetNextObjectInCache()
        {
            GameObject obj = null;

            for (int i = 0; i < objects.Count; i++)
            {
                obj = objects[cacheIndex];

                if (!obj.active)
                    break;

                cacheIndex = (cacheIndex + 1) % objects.Count;
            }

            if (obj.active && objects.Count < maxSize)
            {
                obj = MonoBehaviour.Instantiate(prefab) as GameObject;
                obj.SetActiveRecursively(false);
                obj.name = obj.name + (objects.Count - 1);
                objects.Add(obj);
            }
            else if (obj.active && objects.Count >= maxSize)
            {
                Debug.LogWarning(
                    "Spawn of " + prefab.name +
                    " exceeds cache size of " + maxSize +
                    "! Reusing already active object.", obj);
                MySpawner.Destroy(obj);
            }

            cacheIndex = (cacheIndex + 1) % objects.Count;

            return obj;
        }

        internal int GetCount()
        {
            return objects.Count;
        }

    }
}
